Skip to content

Return every barcode detected in a frame from onReadCode - #821

Open
kimja7045 wants to merge 2 commits into
teslamotors:masterfrom
kimja7045:feat/read-multiple-codes
Open

kimja7045 wants to merge 2 commits into
teslamotors:masterfrom
kimja7045:feat/read-multiple-codes

Conversation

@kimja7045

@kimja7045 kimja7045 commented Oct 7, 2026 •

Copy link
Copy Markdown

Summary

Motivation

Some scanning workflows need several codes that are visible at the same time. In our case, a delivery driver scans a location QR (on a fridge) and a shipping-label QR in a single shot.

onReadCode can only deliver one code per frame today, even though both native scanners already detect every code in the frame:

  • Android: MLKit BarcodeScanning.process() returns a list, and CKCamera filters it by allowedBarcodeTypes / barcodeFrameSize. onBarcodeRead() then dispatches only barcodes.first().
  • iOS: AVCaptureMetadataOutput delivers metadataObjects for every code in the frame, but metadataOutput(_:didOutput:from:) forwards only metadataObjects.first.

The extra codes are computed and then thrown away. This PR forwards all of them. It is a feature addition, not a bug fix.

API change (additive)

onReadCode={(event) => {
  event.nativeEvent.codeStringValue; // unchanged – first code
  event.nativeEvent.codeFormat;      // unchanged – first code
  event.nativeEvent.codes;           // new – [{ codeStringValue, codeFormat }, ...] for every code in the frame
}}

Backward compatibility

The event goes from carrying one code to carrying many, without changing anything existing single-code consumers rely on:

Concern Before After
nativeEvent.codeStringValue / codeFormat first code in the frame same value – still the first code, in the same native order
Events per frame 1 1 – every code rides in the same event via codes. We deliberately did not emit one event per code, so apps that de-duplicate or lock after the first onReadCode keep working
scanThrottleDelay, allowedBarcodeTypes, barcodeFrameSize applied applied identically, before dispatch
Old architecture (Paper) dictionary / WritableMap same path; codes is one extra key
New architecture (Fabric) codegen OnReadCode codegen adds codes: OnReadCodeCodes[]. onFaceDetected already uses array-of-object event payloads, so there is no new minimum React Native version
Android ReadCodeEvent (public class) (surfaceId, viewId, codeStringValue, codeFormat) that constructor is kept and delegates to the new list-based one
iOS internals (CameraProtocol, ScannedCode) – internal only; no public Swift API change

Two points worth a reviewer's call:

  • TypeScript: codes is typed as required because native always sends it. Code that only reads the event is unaffected. Code that builds an OnReadCodeData by hand, such as test mocks, needs codes: []. I'm happy to make it optional if you prefer.
  • iOS edge case: previously, a frame was dropped entirely if its first metadata object had a nil stringValue. Now that object is skipped, and the remaining valid codes are still reported.

Suggested release: minor.

Implementation

  • src/specs/CameraNativeComponent.ts, src/CameraProps.ts: add codes to the event type. This follows the existing onFaceDetected / faces pattern.
  • Android: ReadCodeEvent takes a List<ReadCodePayload> and writes codes plus the first code's top-level fields. CKCamera.onBarcodeRead() maps every filtered barcode.
  • iOS: the onBarcodeRead closure (CameraProtocol / RealCamera / SimulatorCamera) now passes [ScannedCode]. RealCamera uses compactMap over all metadata objects, and CameraView emits codes alongside the existing fields.
  • iOS Fabric: CKCameraViewComponentView.mm builds the codegen OnReadCodeCodes vector, the same way it builds OnFaceDetectedFaces.
  • Docs and example: the README onReadCode row and example/src/BarcodeScreenExample.tsx now show codes.

How did you test this change?

Static checks

  • tsc --noEmit passes for the library and the example app.
  • iOS codegen (react-native codegen --platform ios): the generated EventEmitters.h contains struct OnReadCodeCodes { codeStringValue; codeFormat; } and struct OnReadCode { codeStringValue; codeFormat; std::vector<OnReadCodeCodes> codes; }. These match the designated initializers in CKCameraViewComponentView.mm.

Devices (example app → Barcode screen, two QR codes inside the scan frame)

iOS Android
  • iOS (New Arch): two QR codes → codes.length === 2, both values shown
  • Android (New Arch): two QR codes → codes.length === 2, both values shown
  • Single code on both platforms: codeStringValue / codeFormat unchanged, codes.length === 1
  • Old architecture (Paper) build still receives onReadCode

claude added 2 commits October 6, 2026 09:07
Both scanners already detect all codes in a frame (MLKit on Android,
AVCaptureMetadataOutput on iOS) but only the first one was forwarded to JS.
onReadCode now also includes `codes`, an array of { codeStringValue, codeFormat }
for every code in the frame. Top-level codeStringValue/codeFormat still mirror
the first code, so existing consumers are unaffected.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UjZbt9u2A3dueq2sWj2j5Q
Native code constructing ReadCodeEvent with a single code keeps compiling,
and an empty code list no longer throws when building the event payload.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UjZbt9u2A3dueq2sWj2j5Q
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants